Bass degrees#

%load_ext autoreload
%autoreload 2
import os
from collections import Counter, defaultdict

import matplotlib.pyplot as plt
import ms3
import pandas as pd
import plotly.express as px
from dimcat import groupers, plotting
from dimcat.data.resources.utils import make_adjacency_groups

import utils

pd.set_option("display.max_rows", 1000)
pd.set_option("display.max_columns", 500)
RESULTS_PATH = os.path.abspath(os.path.join(utils.OUTPUT_FOLDER, "bass_degrees"))
os.makedirs(RESULTS_PATH, exist_ok=True)


def make_output_path(
    filename: str,
    extension=None,
    path=RESULTS_PATH,
) -> str:
    return utils.make_output_path(filename=filename, extension=extension, path=path)


def save_figure_as(
    fig, filename, formats=("png", "pdf"), directory=RESULTS_PATH, **kwargs
):
    if formats is not None:
        for fmt in formats:
            plotting.write_image(fig, filename, directory, format=fmt, **kwargs)
    else:
        plotting.write_image(fig, filename, directory, **kwargs)

Loading data

D = utils.get_dataset("couperin_concerts", corpus_release="v2.2")
D
Dataset
=======
{'inputs': {'basepath': None,
            'packages': {'couperin_concerts': ["'couperin_concerts.measures' (MuseScoreMeasures)",
                                               "'couperin_concerts.notes' (MuseScoreNotes)",
                                               "'couperin_concerts.expanded' (MuseScoreHarmonies)",
                                               "'couperin_concerts.chords' (MuseScoreChords)",
                                               "'couperin_concerts.metadata' (Metadata)"]}},
 'outputs': {'basepath': None, 'packages': {}},
 'pipeline': []}

Grouping data

grouped_D = groupers.ModeGrouper().process(D)
grouped_D
GroupedDataset
==============
{'inputs': {'basepath': None,
            'packages': {'couperin_concerts': ["'couperin_concerts.measures' (MuseScoreMeasures)",
                                               "'couperin_concerts.notes' (MuseScoreNotes)",
                                               "'couperin_concerts.expanded' (MuseScoreHarmonies)",
                                               "'couperin_concerts.chords' (MuseScoreChords)",
                                               "'couperin_concerts.metadata' (Metadata)"]}},
 'outputs': {'basepath': None, 'packages': {}},
 'pipeline': ['ModeGrouper']}
bass_notes = grouped_D.get_feature("bassnotes")
bass_notes.df
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label pedal chord numeral form figbass changes relativeroot cadence phraseend chord_type chord_tones added_tones root alt_label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode root_roman relativeroot_resolved effective_localkey effective_localkey_resolved effective_localkey_is_minor pedal_resolved chord_and_mode chord_reduced chord_reduced_and_mode applied_to_numeral numeral_or_applied_to_numeral intervals_over_bass intervals_over_root scale_degrees scale_degrees_and_mode scale_degrees_major scale_degrees_minor bass_degree bass_degree_and_mode bass_degree_major bass_degree_minor bass_note_over_local_tonic globalkey localkey bass_note
mode corpus piece i
major couperin_concerts c01n01_prelude 0 1 0 0 2.00 0 1/2 4/4 1 1 <NA> G.I{ <NA> I I <NA> <NA> <NA> <NA> <NA> { M (0, 4, 1) () 0 <NA> False False major major I I, major I NaN I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) 1 1, major 1 1 P1 G I 0
1 2 1 2 2.00 0 0 4/4 1 1 <NA> V <NA> V V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 <NA> False False major major I I, major V NaN I I False <NA> V, major V V, major <NA> V (M3, P5) (M3, P5) (5, 7, 2) (5, 7, 2), major (5, 7, 2) (5, #7, 2) 5 5, major 5 5 P5 G I 1
2 2 1 4 0.50 1/2 1/2 4/4 1 1 <NA> I6 <NA> I6 I <NA> 6 <NA> <NA> <NA> <NA> M (4, 1, 0) () 0 <NA> False False major major I I, major I NaN I I False <NA> I6, major I6 I6, major <NA> I (m3, m6) (M3, P5) (3, 5, 1) (3, 5, 1), major (3, 5, 1) (#3, 5, 1) 3 3, major 3 #3 M3 G I 4
3 2 1 9/2 0.50 5/8 5/8 4/4 1 1 <NA> I <NA> I I <NA> <NA> <NA> <NA> <NA> <NA> M (0, 4, 1) () 0 <NA> False False major major I I, major I NaN I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) 1 1, major 1 1 P1 G I 0
4 2 1 5 0.75 3/4 3/4 4/4 1 1 <NA> V(4) <NA> V(4) V <NA> <NA> 4 <NA> <NA> <NA> M (1, 0, 2) () 1 <NA> False False major major I I, major V NaN I I False <NA> V(4), major V V, major <NA> V (P4, P5) (P4, P5) (5, 1, 2) (5, 1, 2), major (5, 1, 2) (5, 1, 2) 5 5, major 5 5 P5 G I 1
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
minor couperin_concerts parnasse_07 230 52 52 411/2 0.25 3/8 3/8 4/4 1 1 <NA> i64 <NA> i64 i <NA> 64 <NA> <NA> <NA> <NA> m (1, 0, -3) () 0 <NA> True True minor minor i i, minor i NaN i i True <NA> i64, minor i64 i64, minor <NA> i (P4, m6) (m3, P5) (5, 1, 3) (5, 1, 3), minor (5, 1, b3) (5, 1, 3) 5 5, minor 5 5 P5 b i 1
231 52 52 823/4 0.25 7/16 7/16 4/4 1 1 <NA> iio64 <NA> iio64 ii o 64 <NA> <NA> <NA> <NA> o (-4, 2, -1) () 2 <NA> True True minor minor i i, minor ii NaN i i True <NA> iio64, minor iio64 iio64, minor <NA> ii (a4, M6) (m3, d5) (6, 2, 4) (6, 2, 4), minor (b6, 2, 4) (6, 2, 4) 6 6, minor b6 6 m6 b i -4
232 52 52 206 1.00 1/2 1/2 4/4 1 1 <NA> i6 <NA> i6 i <NA> 6 <NA> <NA> <NA> <NA> m (-3, 1, 0) () 0 <NA> True True minor minor i i, minor i NaN i i True <NA> i6, minor i6 i6, minor <NA> i (M3, M6) (m3, P5) (3, 5, 1) (3, 5, 1), minor (b3, 5, 1) (3, 5, 1) 3 3, minor b3 3 m3 b i -3
233 52 52 207 1.00 3/4 3/4 4/4 1 1 <NA> V <NA> V V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 <NA> True True minor minor i i, minor V NaN i i True <NA> V, minor V V, minor <NA> V (M3, P5) (M3, P5) (5, #7, 2) (5, #7, 2), minor (5, 7, 2) (5, #7, 2) 5 5, minor 5 5 P5 b i 1
234 53 53 208 4.00 0 0 4/4 1 1 <NA> i|PAC} <NA> i i <NA> <NA> <NA> <NA> PAC } m (0, -3, 1) () 0 <NA> True True minor minor i i, minor i NaN i i True <NA> i, minor i i, minor <NA> i (m3, P5) (m3, P5) (1, 3, 5) (1, 3, 5), minor (1, b3, 5) (1, 3, 5) 1 1, minor 1 1 P1 b i 0

8376 rows × 56 columns

bass_notes.plot(
    output=make_output_path("piecewise_bass_degrees"), width=1100, height=2500
)
bass_notes.plot_grouped(output=make_output_path("bass_degrees_major_minor"))

Bass degree unigrams#

As expressed by the annotation labels.

labels = D.get_feature("harmonylabels")
df = labels.df
df.head(20)
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label pedal numeral form figbass changes relativeroot cadence phraseend chord_type chord_tones added_tones root bass_note alt_label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode root_roman relativeroot_resolved effective_localkey effective_localkey_resolved effective_localkey_is_minor pedal_resolved chord_and_mode chord_reduced chord_reduced_and_mode applied_to_numeral numeral_or_applied_to_numeral intervals_over_bass intervals_over_root scale_degrees scale_degrees_and_mode scale_degrees_major scale_degrees_minor globalkey localkey chord
corpus piece i
couperin_concerts c01n01_prelude 0 1 0 0 2.00 0 1/2 4/4 1 1 <NA> G.I{ <NA> I <NA> <NA> <NA> <NA> <NA> { M (0, 4, 1) () 0 0 <NA> False False major major I I, major I <NA> I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) G I I
1 2 1 2 2.00 0 0 4/4 1 1 <NA> V <NA> V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 1 <NA> False False major major I I, major V <NA> I I False <NA> V, major V V, major <NA> V (M3, P5) (M3, P5) (5, 7, 2) (5, 7, 2), major (5, 7, 2) (5, #7, 2) G I V
2 2 1 4 0.50 1/2 1/2 4/4 1 1 <NA> I6 <NA> I <NA> 6 <NA> <NA> <NA> <NA> M (4, 1, 0) () 0 4 <NA> False False major major I I, major I <NA> I I False <NA> I6, major I6 I6, major <NA> I (m3, m6) (M3, P5) (3, 5, 1) (3, 5, 1), major (3, 5, 1) (#3, 5, 1) G I I6
3 2 1 9/2 0.50 5/8 5/8 4/4 1 1 <NA> I <NA> I <NA> <NA> <NA> <NA> <NA> <NA> M (0, 4, 1) () 0 0 <NA> False False major major I I, major I <NA> I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) G I I
4 2 1 5 0.75 3/4 3/4 4/4 1 1 <NA> V(4) <NA> V <NA> <NA> 4 <NA> <NA> <NA> M (1, 0, 2) () 1 1 <NA> False False major major I I, major V <NA> I I False <NA> V(4), major V V, major <NA> V (P4, P5) (P4, P5) (5, 1, 2) (5, 1, 2), major (5, 1, 2) (5, 1, 2) G I V(4)
5 2 1 23/4 0.25 15/16 15/16 4/4 1 1 <NA> V <NA> V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 1 <NA> False False major major I I, major V <NA> I I False <NA> V, major V V, major <NA> V (M3, P5) (M3, P5) (5, 7, 2) (5, 7, 2), major (5, 7, 2) (5, #7, 2) G I V
6 3 2 6 2.00 0 0 4/4 1 1 <NA> I|IAC} <NA> I <NA> <NA> <NA> <NA> IAC } M (0, 4, 1) () 0 0 <NA> False False major major I I, major I <NA> I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) G I I
7 3 2 8 1.00 1/2 1/2 4/4 1 1 <NA> vi{ <NA> vi <NA> <NA> <NA> <NA> <NA> { m (3, 0, 4) () 3 3 <NA> False False major major I I, major vi <NA> I I False <NA> vi, major vi vi, major <NA> vi (m3, P5) (m3, P5) (6, 1, 3) (6, 1, 3), major (6, 1, 3) (#6, 1, #3) G I vi
8 3 2 9 1.00 3/4 3/4 4/4 1 1 <NA> I6 <NA> I <NA> 6 <NA> <NA> <NA> <NA> M (4, 1, 0) () 0 4 <NA> False False major major I I, major I <NA> I I False <NA> I6, major I6 I6, major <NA> I (m3, m6) (M3, P5) (3, 5, 1) (3, 5, 1), major (3, 5, 1) (#3, 5, 1) G I I6
9 4 3 10 1.00 0 0 4/4 1 1 <NA> V6 <NA> V <NA> 6 <NA> <NA> <NA> <NA> M (5, 2, 1) () 1 5 <NA> False False major major I I, major V <NA> I I False <NA> V6, major V6 V6, major <NA> V (m3, m6) (M3, P5) (7, 2, 5) (7, 2, 5), major (7, 2, 5) (#7, 2, 5) G I V6
10 4 3 11 1.00 1/4 1/4 4/4 1 1 <NA> vi%43/V <NA> vi % 43 <NA> V <NA> <NA> %7 (-2, 2, 4, 1) () 4 -2 iii%43 False False major major I I, major vi/V V V/I V False <NA> vi%43/V, major vi%43/V vi%43/V, major V V (M3, a4, M6) (m3, d5, m7) (b7, 2, 3, 5) (b7, 2, 3, 5), major (b7, 2, 3, 5) (7, 2, #3, 5) G I vi%43/V
11 4 3 12 1.00 1/2 1/2 4/4 1 1 <NA> ii7/V <NA> ii <NA> 7 <NA> V <NA> <NA> mm7 (3, 0, 4, 1) () 3 3 <NA> False False major major I I, major ii/V V V/I V False <NA> ii7/V, major ii7/V ii7/V, major V V (m3, P5, m7) (m3, P5, m7) (6, 1, 3, 5) (6, 1, 3, 5), major (6, 1, 3, 5) (#6, 1, #3, 5) G I ii7/V
12 4 3 13 1.00 3/4 3/4 4/4 1 1 <NA> V43/V <NA> V <NA> 43 <NA> V <NA> <NA> Mm7 (3, 0, 2, 6) () 2 3 <NA> False False major major I I, major V/V V V/I V False <NA> V43/V, major V43/V V43/V, major V V (m3, P4, M6) (M3, P5, m7) (6, 1, 2, #4) (6, 1, 2, #4), major (6, 1, 2, #4) (#6, 1, 2, #4) G I V43/V
13 5 4 14 2.00 0 0 4/4 1 1 <NA> V|HC.TEN} <NA> V <NA> <NA> <NA> <NA> HC.TEN } M (1, 5, 2) () 1 1 <NA> False False major major I I, major V <NA> I I False <NA> V, major V V, major <NA> V (M3, P5) (M3, P5) (5, 7, 2) (5, 7, 2), major (5, 7, 2) (5, #7, 2) G I V
14 5 4 16 1.00 1/2 1/2 4/4 1 1 <NA> ii.iio{ <NA> ii o <NA> <NA> <NA> <NA> { o (2, -1, -4) () 2 2 <NA> False True major minor ii ii, major ii <NA> ii ii True <NA> iio, minor iio iio, minor <NA> ii (m3, d5) (m3, d5) (2, 4, 6) (2, 4, 6), minor (2, 4, b6) (2, 4, 6) G ii iio
15 5 4 17 1.00 3/4 3/4 4/4 1 1 <NA> #viio7 <NA> #vii o 7 <NA> <NA> <NA> <NA> o7 (5, 2, -1, -4) () 5 5 <NA> False True major minor ii ii, major #vii <NA> ii ii True <NA> #viio7, minor #viio7 #viio7, minor <NA> #vii (m3, d5, d7) (m3, d5, d7) (#7, 2, 4, 6) (#7, 2, 4, 6), minor (7, 2, 4, b6) (#7, 2, 4, 6) G ii #viio7
16 6 5 18 1.00 0 0 4/4 1 1 <NA> III+M7(9) <NA> III +M 7 9 <NA> <NA> <NA> +M7 (-3, 1, 5, 2) (-1,) -3 -3 <NA> False True major minor ii ii, major III <NA> ii ii True <NA> III+M7(9), minor III+M7 III+M7, minor <NA> III (M3, a5, M7) (M3, a5, M7) (3, 5, #7, 2) (3, 5, #7, 2), minor (b3, 5, 7, 2) (3, 5, #7, 2) G ii III+M7(9)
17 6 5 19 1.00 1/4 1/4 4/4 1 1 <NA> i6 <NA> i <NA> 6 <NA> <NA> <NA> <NA> m (-3, 1, 0) () 0 -3 <NA> False True major minor ii ii, major i <NA> ii ii True <NA> i6, minor i6 i6, minor <NA> i (M3, M6) (m3, P5) (3, 5, 1) (3, 5, 1), minor (b3, 5, 1) (3, 5, 1) G ii i6
18 6 5 20 1.00 1/2 1/2 4/4 1 1 <NA> ii65 <NA> ii <NA> 65 <NA> <NA> <NA> <NA> mm7 (-1, 3, 0, 2) () 2 -1 <NA> False True major minor ii ii, major ii <NA> ii ii True <NA> ii65, minor ii65 ii65, minor <NA> ii (M3, P5, M6) (m3, P5, m7) (4, #6, 1, 2) (4, #6, 1, 2), minor (4, 6, 1, 2) (4, #6, 1, 2) G ii ii65
19 6 5 21 0.50 3/4 3/4 4/4 1 1 <NA> V64 <NA> V <NA> 64 <NA> <NA> <NA> <NA> M (2, 1, 5) () 1 2 <NA> False True major minor ii ii, major V <NA> ii ii True <NA> V64, minor V64 V64, minor <NA> V (P4, M6) (M3, P5) (2, 5, #7) (2, 5, #7), minor (2, 5, 7) (2, 5, #7) G ii V64
bd = df.bass_note.value_counts()
print(f"N = {len(df)}")
fig = px.bar(
    x=ms3.fifths2sd(bd.index.to_list()),
    y=bd.values,
    labels=dict(x="Scale degree in relation to major scale", y="count"),
    title="Distribution of all bass degrees",
    color_discrete_sequence=["grey"] * len(bd),
)
fig.update_xaxes(type="category")
save_figure_as(fig, "bass_degree_unigrams")
fig.show()
N = 8376
localkey_is_major = ~df.localkey_is_minor
print(f"N = {localkey_is_major.sum()}")
bd_maj = df[localkey_is_major].bass_note.value_counts()
fig = px.bar(
    bd_maj,
    x=ms3.fifths2sd(bd_maj.index.to_list(), minor=False),
    y=bd_maj.values,
    labels=dict(x="Scale degree in relation to major scale", y="count"),
    title="Distribution of bass degrees in major segments",
    color_discrete_sequence=["blue"] * len(bd_maj),
)
fig.update_xaxes(type="category")
save_figure_as(fig, "bass_degree_unigrams_major")
fig.show()
N = 4138
print(f"N = {df.localkey_is_minor.sum()}")
bd_min = df[df.localkey_is_minor].bass_note.value_counts()
fig = px.bar(
    bd_min,
    x=ms3.fifths2sd(bd_min.index.to_list(), minor=True),
    y=bd_min.values,
    labels=dict(x="Scale degree in relation to minor scale", y="count"),
    title="Distribution of bass degrees in minor segments",
    color_discrete_sequence=["red"] * len(bd_min),
)
fig.update_xaxes(type="category")
save_figure_as(fig, "bass_degree_unigrams_minor")
fig.show()
N = 4238

Note: When dropping immediate repetitions of the same bass degree (see transition matrices below), minor segments, too, have 1 as the most frequent bass degree. This can be linked to frequent suspensions over bass degree 5.

Intervals between adjacent bass notes#

Preparing the data#

Get localkey segments#

df.groupby(level=0, group_keys=False).localkey.apply(
    lambda col: col != col.shift()
).cumsum()
corpus             piece           i  
couperin_concerts  c01n01_prelude  0      <NA>
                                   1         0
                                   2         0
                                   3         0
                                   4         0
                                          ... 
                   parnasse_07     230     516
                                   231     516
                                   232     516
                                   233     516
                                   234     516
Name: localkey, Length: 8376, dtype: Int64
key_region_groups, key_region2key = make_adjacency_groups(df.localkey, groupby="piece")
df["key_regions"] = key_region_groups
df["bass_degree"] = ms3.transform(df, ms3.fifths2sd, ["bass_note", "localkey_is_minor"])
segment_lengths = df.groupby("key_regions").size()
fig = px.histogram(
    segment_lengths,
    log_y=True,
    labels=dict(value="#labels in a key segment", count="number of key segments"),
)
save_figure_as(fig, "n_labels_per_key_segment_histogram")
fig.show()
# Show all segments of length L
L = 1
selected = segment_lengths[segment_lengths == L].index
df[df.key_regions.isin(selected)]
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label pedal numeral form figbass changes relativeroot cadence phraseend chord_type chord_tones added_tones root bass_note alt_label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode root_roman relativeroot_resolved effective_localkey effective_localkey_resolved effective_localkey_is_minor pedal_resolved chord_and_mode chord_reduced chord_reduced_and_mode applied_to_numeral numeral_or_applied_to_numeral intervals_over_bass intervals_over_root scale_degrees scale_degrees_and_mode scale_degrees_major scale_degrees_minor globalkey localkey chord key_regions bass_degree
corpus piece i
couperin_concerts parnasse_06 33 13 13 37/2 6.5 1/8 1/8 3/8 1 1 <NA> iii.i <NA> i <NA> <NA> <NA> <NA> <NA> <NA> m (0, -3, 1) () 0 0 <NA> False True major minor iii iii, major i <NA> iii iii True <NA> i, minor i i, minor <NA> i (m3, P5) (m3, P5) (1, 3, 5) (1, 3, 5), minor (1, b3, 5) (1, 3, 5) D iii i 551 1

Delete @none labels This creates progressions between the label before and after the @none label that might not actually be perceived as transitions!

df = utils.remove_none_labels(df)
Length before: 8376
There are 0 @none labels which we are going to delete.
Length after: 8376

Delete non-chord labels (typically, phrase labels)

df = utils.remove_non_chord_labels(df)
Length before: 8376
There are 0 non-chord labels which we are going to delete:
Series([], Name: count, dtype: Int64)
Length after: 8376

Get bass degree progressions & intervals#

All scale degrees are expressed as fifth-intervals to the local tonic:

fifths-interval

interval

-3

m3

-2

m7

-1

P4

0

local tonic

1

P5

2

M2

3

M6

bd_series = {seg: bn for seg, bn in df.groupby("key_regions").bass_note}
bd_intervals = {seg: bd - bd.shift() for seg, bd in bd_series.items()}
df["bass_interval"] = df.groupby("key_regions", group_keys=False).bass_note.apply(
    lambda bd: bd.shift(-1) - bd
)
print("Example output for the intervals of the first key segment:")
df.loc[df.key_regions == 1, ["bass_note", "bass_interval"]]
Example output for the intervals of the first key segment:
bass_note bass_interval
corpus piece i
couperin_concerts c01n01_prelude 0 0 1
1 1 3
2 4 -4
3 0 1
4 1 0
5 1 -1
6 0 3
7 3 1
8 4 1
9 5 -7
10 -2 5
11 3 0
12 3 -2
13 1 <NA>

Count bass intervals#

interval_counter = Counter()
for S in bd_intervals.values():
    interval_counter.update(S.dropna())
interval_counter
Counter({-1: 1422,
         0: 917,
         2: 852,
         -2: 788,
         -5: 725,
         3: 704,
         1: 599,
         5: 587,
         -4: 549,
         4: 286,
         -3: 207,
         6: 53,
         8: 49,
         7: 25,
         -8: 22,
         -7: 14,
         -6: 7,
         9: 2,
         -9: 2})
bar_data = pd.Series(interval_counter).sort_values(ascending=False)
fig = px.bar(
    x=ms3.fifths2iv(bar_data.index.to_list()),
    y=bar_data.values,
    labels=dict(x="Interval between adjacent bass notes", y="count"),
    title="Distribution of all bass intervals (within the same localkey)",
    color_discrete_sequence=["grey"] * len(bd),
)
fig.update_xaxes(type="category")
save_figure_as(fig, "bass_progression_intervals_within_all_key_segments_sorted_bars")
fig.show()

**

px.bar(
    x=interval_counter.keys(),
    y=interval_counter.values(),
    labels=dict(x="interval in fifths", y="count"),
    title="Orderd on the line of fifths",
)
iv_order = [
    -6,
    1,
    8,
    -11,
    -4,
    3,
    10,
    -9,
    -2,
    5,
    12,
    -7,
    0,
    7,
    -5,
    2,
    9,
    -10,
    -3,
    4,
    11,
    -8,
    -1,
    6,
]  # do not occur: -11, -10, 11
iv_counter = {
    ms3.fifths2iv(i, True): interval_counter[i]
    for i in iv_order
    if i in interval_counter
}
px.bar(
    x=iv_counter.keys(),
    y=iv_counter.values(),
    labels=dict(x="interval", y="count"),
    title="Ordered by base " "interval",
)
iv_order = [
    12,
    10,
    -9,
    -6,
    -7,
    8,
    -4,
    5,
    1,
    3,
    -2,
    0,
    -1,
    2,
    -5,
    4,
    -3,
    6,
    7,
    -8,
    9,
]  # do not occur: -11, -10, 11
iv_counter = {
    ms3.fifths2iv(i): interval_counter[i] for i in iv_order if i in interval_counter
}
px.bar(
    x=iv_counter.keys(),
    y=iv_counter.values(),
    labels=dict(x="interval", y="count"),
    title="Ordered around the unison",
)
px.bar(
    x=sorted(iv_counter.keys(), key=lambda k: iv_counter[k], reverse=True),
    y=sorted(iv_counter.values(), reverse=True),
    labels=dict(x="interval", y="count"),
    title="Descending frequency",
)

Checking rare intervals#

occurrences of -9 (-A2)

  • c02n01_prelude m. 12: ii% was corrected to iii%

The other two were correct:

  • c07n05_gavote m. 27: progression V65 VIM7 in minor

  • c03n05_gavotte m. 22: progression V65 ii%43 (in minor)

# see all key regions containing a certain interval in the bass
bass_interval = -9
selected = df.loc[df.bass_interval == bass_interval].key_regions.unique()
df.loc[
    df.key_regions.isin(selected), ["mc", "mn", "chord", "bass_degree", "bass_interval"]
]
mc mn chord bass_degree bass_interval
corpus piece i
couperin_concerts c03n05_gavotte 52 19 16 VII6 2 2
53 19 16 V65/IV #3 -5
54 19 16 IV 4 3
55 19 16 V43 2 -5
56 20 17 i6 3 2
57 20 17 ii%65 4 2
58 20 17 V 5 4
59 21 18 V65 #7 -4
60 21 18 V7 5 -1
61 21 18 i 1 1
62 22 19 V 5 -1
63 22 19 i 1 2
64 23 20 V43 2 -5
65 23 20 i6 3 8
66 23 20 V65 #7 -5
67 24 21 i 1 -3
68 24 21 i6 3 8
69 24 21 V65 #7 -5
70 25 22 i 1 2
71 25 22 V43 2 -5
72 25 22 i6 3 8
73 25 22 V65 #7 -9
74 25 22 ii%43 6 5
75 26 23 i64 5 -4
76 26 23 i6 3 5
77 26 23 V43 2 -5
78 26 23 i6 3 8
79 26 23 V65 #7 -5
80 27 24 i 1 -3
81 27 24 i6 3 2
82 27 24 iv 4 2
83 27 24 V 5 -1
84 28 25 i 1 <NA>
c07n05_gavote 33 22 20 iv 4 3
34 23 21 iio 2 3
35 23 21 V65 #7 -4
36 23 21 V7 5 -1
37 24 22 i 1 1
38 24 22 V 5 -1
39 24 22 i 1 -4
40 25 23 iv6 6 4
41 26 24 i 1 1
42 27 25 V 5 -1
43 27 25 i 1 -4
44 27 25 VI 6 3
45 27 25 ii%65 4 2
46 28 26 V 5 -2
47 28 26 V2 4 -2
48 28 26 i6 3 3
49 28 26 i7 1 -1
50 28 26 iv 4 3
51 28 26 iio 2 3
52 29 27 V65 #7 -9
53 29 27 VIM7 6 5
54 29 27 V 5 0
55 29 27 V7 5 -1
56 30 28 i 1 <NA>

Inspecting stepwise bass movement#

Add column with bass interval in semitones. It’s called bass_interval_pc as in “pitch class”

pc_ivs = ms3.transform(df, ms3.fifths2pc, ["bass_interval"])
df["bass_interval_pc"] = pc_ivs.where(pc_ivs <= 6, pc_ivs % -6)
df[["mc", "mn", "chord", "bass_degree", "bass_interval", "bass_interval_pc"]].head(15)
mc mn chord bass_degree bass_interval bass_interval_pc
corpus piece i
couperin_concerts c01n01_prelude 0 1 0 I 1 1 -5
1 2 1 V 5 3 -3
2 2 1 I6 3 -4 -4
3 2 1 I 1 1 -5
4 2 1 V(4) 5 0 0
5 2 1 V 5 -1 5
6 3 2 I 1 3 -3
7 3 2 vi 6 1 -5
8 3 2 I6 3 1 -5
9 4 3 V6 7 -7 -1
10 4 3 vi%43/V b7 5 -1
11 4 3 ii7/V 6 0 0
12 4 3 V43/V 6 -2 -2
13 5 4 V 5 <NA> <NA>
14 5 4 iio 2 3 -3

Create key region summary#

With one row per key region and the following columns

column

what it contains

globalkey

the global key of the segment

localkey

the local key of the segment

length

number of labels

length_norepeat

number of labels without immediate repetitions

n_stepwise

number of stepwise bass progressions

%_stepwise

percentage of stepwise progressions (based on length_norepeat)

n_ascending

number of stepwise ascending bass progressions

n_descending

number of stepwise descending bass progressions

bd

full sequence of bass degrees

stepwise_bd

all sequences of consecutive stepwise bass progressions

stepwise_chords

the corresponding chord sequences

ascending_bd

the subset of ascending bass progressions

ascending_chords

the corresponding chord sequences

descending_bd

the subset of descending bass progressions

descending_chords

the corresponding chord sequences

ixa

index of the segment’s first row

ixb

index of the segment’s last row

key_regions = utils.make_key_region_summary_table(df, "key_regions")
key_regions.head(10)
globalkey localkey length length_norepeat n_stepwise %_stepwise n_ascending n_descending bd stepwise_bd stepwise_chords ascending_bd ascending_chords descending_bd descending_chords ixa ixb
key_regions
1 G I 14 12 3 25.0 0 3 1 5 3 1 5 1 6 3 7 b7 6 5 [7 b7 6 6] [V6 vi%43/V ii7/V V43/V] [] [] [7 b7 6 6] [V6 vi%43/V ii7/V V43/V] (couperin_concerts, c01n01_prelude, 0) (couperin_concerts, c01n01_prelude, 13)
2 G ii 8 7 1 14.3 1 0 2 #7 3 4 2 5 1 [3 3 4] [III+M7(9) i6 ii65] [3 3 4] [III+M7(9) i6 ii65] [] [] (couperin_concerts, c01n01_prelude, 14) (couperin_concerts, c01n01_prelude, 21)
3 G V 13 10 7 70.0 2 5 5 4 3 2 1 4 3 4 5 1 [5 5 4 3 2 1, 4 3 4 5 5 5] [V I64 V2 I6 V43 vi6, V2 I6 ii65 V(64) V V7] [3 4 5 5 5] [I6 ii65 V(64) V V7] [5 5 4 3 2 1, 4 3] [V I64 V2 I6 V43 vi6, V2 I6] (couperin_concerts, c01n01_prelude, 22) (couperin_concerts, c01n01_prelude, 34)
4 G IV 6 4 1 25.0 0 1 4 3 5 1 [4 4 3] [ii6 V2 I6] [] [] [4 4 3] [ii6 V2 I6] (couperin_concerts, c01n01_prelude, 35) (couperin_concerts, c01n01_prelude, 40)
5 G V 7 7 3 42.9 1 2 5 3 2 1 5 7 1 [3 2 1, 7] [I6 V43 I, V65] [7] [V65] [3 2 1] [I6 V43 I] (couperin_concerts, c01n01_prelude, 41) (couperin_concerts, c01n01_prelude, 47)
6 G I 45 36 19 52.8 9 10 7 1 2 7 1 6 5 4 3 4 2 1 5 3 2 1 4 3 5 1 6 5 4 ... [7 1 2, 7 1, 6 5 4 3 4, 2 1, 3 2 1, 4 4 4 3, 6... [V65 I V43, V6 I, vi I64 viio64 I6 IV, V43 I, ... [7 1 2, 7 1, 3 4, 4 #4 5 6 7 1] [V65 I V43, V6 I, I6 IV, IVM7 V65/V V vi7 V65 I] [6 5 4 3, 2 1, 3 2 1, 4 4 4 3, 6 6 5 5 4, 4 4 ... [vi I64 viio64 I6, V43 I, I6 V43 I, IVM7 ii65 ... (couperin_concerts, c01n01_prelude, 48) (couperin_concerts, c01n01_prelude, 93)
7 G I 15 11 4 36.4 2 2 1 7 5 6 5 1 5 1 2 5 1 [1 7, 5 6 6 5, 1 1 2] [I V6, V IV6(2) IV6 V7, I I ii7] [5 6 6, 1 1 2] [V IV6(2) IV6, I I ii7] [1 7, 6 6 5] [I V6, IV6(2) IV6 V7] (couperin_concerts, c01n02_allemande, 0) (couperin_concerts, c01n02_allemande, 14)
8 G V 22 22 13 59.1 9 4 3 1 5 7 1 7 1 5 6 4 5 1 7 1 7 1 7 1 3 4 5 1 [7 1 7 1, 5 6, 4 5, 1 7 1 7 1 7 1, 3 4 5] [V6 I V6 I, V7 vi, IVM7 V, I V65 I V65 I V65 I... [7 1, 7 1, 5 6, 4 5, 7 1, 7 1, 7 1, 3 4 5] [V6 I, V6 I, V7 vi, IVM7 V, V65 I, V65 I, V65 ... [1 7, 1 7, 1 7, 1 7] [I V6, I V65, I V65, I V65] (couperin_concerts, c01n02_allemande, 15) (couperin_concerts, c01n02_allemande, 37)
9 G I 3 3 1 33.3 1 0 5 3 4 [3] [I6] [3] [I6] [] [] (couperin_concerts, c01n02_allemande, 38) (couperin_concerts, c01n02_allemande, 40)
10 G vi 12 11 6 54.5 4 2 4 5 3 1 4 3 4 3 4 5 1 [4 5, 4 3 4 3 4 5 5] [ii%65 V, V2 i6 V2 i6 ii65 V(64) V] [4 5, 3 4, 3 4 5 5] [ii%65 V, i6 V2, i6 ii65 V(64) V] [4 3, 4 3] [V2 i6, V2 i6] (couperin_concerts, c01n02_allemande, 41) (couperin_concerts, c01n02_allemande, 52)

Store to file key_regions.tsv for easier inspection.

key_regions.to_csv(os.path.join(RESULTS_PATH, "key_regions.tsv"), sep="\t")
print(
    f"{key_regions.n_stepwise.sum() / key_regions.length_norepeat.sum():.1%} of all bass movements are stepwise."
)
40.1% of all bass movements are stepwise.
def remove_immediate_repetitions(s):
    res = []
    last = ""
    for word in s.split():
        if word != last:
            res.append(word)
        last = word
    return " ".join(res)
minor_selector = key_regions.localkey.str.islower()
minor_regions = key_regions[minor_selector]
major_regions = key_regions[~minor_selector]

All stepwise ascending bass progressions in minor#

ascending_minor = defaultdict(list)
for bd, chord in zip(
    minor_regions.ascending_bd.sum(), minor_regions.ascending_chords.sum()
):
    ascending_minor[remove_immediate_repetitions(bd)].append(chord)
ascending_minor_counts = Counter({k: len(v) for k, v in ascending_minor.items()})
utils.prettify_counts(ascending_minor_counts)
N = 604
count %
progression
4 5 143 23.68
#7 1 113 18.71
3 4 5 40 6.62
2 3 40 6.62
5 6 33 5.46
3 4 32 5.30
#3 4 26 4.30
1 2 18 2.98
4 5 6 17 2.81
#7 13 2.15
1 2 3 10 1.66
2 3 4 5 9 1.49
#3 4 5 6 7 1.16
5 #6 #7 1 7 1.16
#7 1 2 3 7 1.16
5 7 1.16
3 4 5 6 7 1.16
#6 #7 1 6 0.99
7 1 5 0.83
6 7 5 0.83
2 3 4 5 6 4 0.66
5 #6 #7 3 0.50
#3 4 5 3 0.50
2 #3 4 3 0.50
2 3 4 3 0.50
1 2 3 4 5 3 0.50
#3 3 0.50
#7 1 2 3 4 5 3 0.50
#7 1 2 3 4 2 0.33
2 2 0.33
5 #6 2 0.33
#6 #7 2 0.33
3 4 5 #6 #7 1 2 0.33
#4 5 2 0.33
#7 1 2 2 0.33
#3 4 5 #6 1 0.17
4 5 6 #6 7 #7 1 1 0.17
3 4 #4 5 6 1 0.17
4 #4 5 6 1 0.17
3 #3 4 5 1 0.17
3 4 5 #6 #7 1 0.17
3 1 0.17
4 5 #6 #7 1 0.17
1 2 #3 4 1 0.17
4 1 0.17
#3 4 #4 5 6 1 0.17
1 2 3 #3 4 1 0.17
4 #4 5 1 0.17
4 5 6 #6 7 #7 1 0.17
5 #6 #7 1 2 1 0.17
#6 1 0.17
4 5 #6 1 0.17
2 #3 1 0.17
5 6 #6 7 #7 1 1 0.17
5 #6 #7 1 2 #3 4 1 0.17
show_progression = "3 4 5"
chords_3_4_5 = Counter(ascending_minor[show_progression])
utils.prettify_counts(chords_3_4_5)
N = 40
count %
progression
i6 ii%65 V 8 20.0
i6 ii%65 V V7 7 17.5
i6 ii%65 V(4) V 7 17.5
i6 ii%65 V(64) V 5 12.5
i6 ii65 V V7 2 5.0
i6 ii65 V(64) V 1 2.5
i6 ii%65 V(64) V7 1 2.5
i6 iv V 1 2.5
i6 IV iio6 V 1 2.5
i6 IV7 V 1 2.5
i6 iv7 ii%65 V 1 2.5
i6 iv ii6 V 1 2.5
i6 iv7(9) V(4) V 1 2.5
i6 ii%65 V(64) 1 2.5
III ii%65 V 1 2.5
i6 IV7(9) ii6 V7 1 2.5

All stepwise ascending bass progressions in major#

ascending_major = defaultdict(list)
for bd, chord in zip(
    major_regions.ascending_bd.sum(), major_regions.ascending_chords.sum()
):
    ascending_major[remove_immediate_repetitions(bd)].append(chord)
ascending_major_counts = Counter({k: len(v) for k, v in ascending_major.items()})
utils.prettify_counts(ascending_major_counts)
N = 524
count %
progression
7 1 115 21.95
4 5 101 19.27
3 4 60 11.45
3 4 5 34 6.49
5 6 32 6.11
6 7 1 30 5.73
1 2 22 4.20
2 3 16 3.05
7 15 2.86
1 2 3 11 2.10
5 6 7 9 1.72
5 6 7 1 9 1.72
#4 5 8 1.53
3 4 5 6 7 1.34
3 6 1.15
6 7 5 0.95
3 4 #4 5 4 0.76
7 1 2 3 0.57
4 #4 5 3 0.57
2 3 4 5 3 0.57
3 4 5 6 7 1 3 0.57
1 2 3 4 5 3 0.57
4 5 6 2 0.38
2 3 4 2 0.38
4 5 6 7 1 2 0.38
1 2 3 4 2 0.38
4 5 6 7 2 0.38
3 4 #4 5 6 7 1 2 0.38
4 #4 5 6 7 1 1 0.19
7 1 2 3 1 0.19
#1 2 1 0.19
2 1 0.19
5 #5 6 1 0.19
3 #4 1 0.19
3 4 5 6 7 1 0.19
1 2 3 4 5 6 7 1 1 0.19
5 #5 6 7 1 1 0.19
7 #1 2 1 0.19
1 #1 2 1 0.19
7 #1 1 0.19
2 3 #4 5 6 7 1 1 0.19
show_progression = "6 7 1"
chords_6_7_1 = Counter(ascending_major[show_progression])
utils.prettify_counts(chords_6_7_1)
N = 30
count %
progression
IV6 V65 I 8 26.67
IV6 V65 I(4) I 4 13.33
vi V65 I 2 6.67
IV6 V65 I ii2 2 6.67
vi V65 I(4) I 1 3.33
IV6(2) IV6 V65 I 1 3.33
vi7 IV6 V65 I 1 3.33
IV6 V6 I IV64 1 3.33
vi IV6 V65 I 1 3.33
vi7 IV6 vii%7 V65 I 1 3.33
IV6 V65 I(4) 1 3.33
IVM65 V65 I(9) 1 3.33
vi7 V65 I 1 3.33
IV6 viio I 1 3.33
IVM65 V65 I(9) I 1 3.33
IV65 V65 I(9) I 1 3.33
vi7 V65 I(94) 1 3.33
IV6 V65(9) V65 I 1 3.33

All stepwise descending bass progressions in minor#

descending_minor = defaultdict(list)
for bd, chord in zip(
    minor_regions.descending_bd.sum(), minor_regions.descending_chords.sum()
):
    descending_minor[remove_immediate_repetitions(bd)].append(chord)
descending_minor_counts = Counter({k: len(v) for k, v in descending_minor.items()})
utils.prettify_counts(descending_minor_counts)
N = 400
count %
progression
1 #7 49 12.25
6 5 39 9.75
4 3 36 9.00
3 2 34 8.50
3 2 1 24 6.00
5 4 3 21 5.25
1 7 6 18 4.50
1 7 6 5 16 4.00
5 4 15 3.75
2 1 14 3.50
3 2 1 #7 12 3.00
4 3 2 1 11 2.75
1 7 10 2.50
5 4 3 2 9 2.25
6 5 4 8 2.00
2 1 #7 7 1.75
5 4 3 2 1 7 1.75
4 3 2 1 #7 6 1.50
6 6 1.50
4 3 2 6 1.50
6 5 4 3 5 1.25
5 4 3 2 1 #7 5 1.25
6 5 4 3 2 1 5 1.25
6 5 4 3 2 4 1.00
4 #3 3 0.75
1 7 6 5 4 3 3 0.75
#6 5 2 0.50
1 7 6 5 4 3 2 2 0.50
1 2 0.50
1 7 #6 5 2 0.50
2 2 0.50
6 5 4 3 2 1 #7 2 0.50
#7 7 6 5 4 1 0.25
5 1 0.25
5 #4 1 0.25
#6 1 0.25
6 5 4 #3 1 0.25
7 6 5 4 3 1 0.25
#6 6 5 1 0.25
2 1 #7 7 #6 6 1 0.25
#7 7 6 1 0.25
5 #4 4 3 1 0.25
5 4 #3 1 0.25
7 6 5 4 1 0.25
1 #7 7 6 5 1 0.25
7 6 1 0.25
4 3 2 1 7 6 5 1 0.25
show_progression = "3 2 1"
chords_3_2_1 = Counter(descending_minor[show_progression])
utils.prettify_counts(chords_3_2_1)
N = 24
count %
progression
i6 V43 i 17 70.83
III+7 i6 V43 i 1 4.17
III+ i6 V43 i 1 4.17
IV2 V43 i 1 4.17
i6 ii%7 V43 i i 1 4.17
#vio64 #viio6 i 1 4.17
III+ #vio64 #viio6 i 1 4.17
i6 #viio6 i 1 4.17

All stepwise descending bass progressions in major#

descending_major = defaultdict(list)
for bd, chord in zip(
    major_regions.descending_bd.sum(), major_regions.descending_chords.sum()
):
    descending_major[remove_immediate_repetitions(bd)].append(chord)
descending_major_counts = Counter({k: len(v) for k, v in descending_major.items()})
utils.prettify_counts(descending_major_counts)
N = 354
count %
progression
1 7 67 18.93
4 3 44 12.43
6 5 31 8.76
2 1 29 8.19
5 4 3 20 5.65
3 2 1 19 5.37
1 7 6 5 17 4.80
4 3 2 1 16 4.52
4 3 2 14 3.95
1 7 6 12 3.39
3 2 12 3.39
3 2 1 7 9 2.54
5 4 3 2 1 5 1.41
6 5 4 5 1.41
5 4 3 2 5 1.41
5 4 5 1.41
b7 6 5 1.41
6 5 4 3 3 0.85
1 7 b7 6 3 0.85
2 1 7 3 0.85
1 3 0.85
7 6 3 0.85
7 b7 6 2 0.56
6 2 0.56
1 7 6 5 4 3 2 0.56
1 b7 6 2 0.56
6 5 4 3 2 2 0.56
4 3 2 1 7 2 0.56
1 b7 6 5 4 1 0.28
7 6 5 #4 1 0.28
4 3 2 1 7 6 5 1 0.28
1 7 6 5 4 3 2 1 1 0.28
5 4 3 2 1 7 b7 6 1 0.28
5 4 3 2 1 7 6 1 0.28
1 7 b7 1 0.28
2 1 7 6 5 1 0.28
7 6 5 1 0.28
3 2 1 7 6 5 4 3 1 0.28
2 1 0.28
7 6 5 4 3 2 1 1 0.28
show_progression = "5 4 3"
chords_5_4_3 = Counter(descending_major[show_progression])
utils.prettify_counts(chords_5_4_3)
N = 20
count %
progression
V V2 I6 15 75.0
V V2 V65/IV 1 5.0
I64 V2 I6 1 5.0
I64 V I64 ii6(2) ii6 I6(2) I6 1 5.0
I64 V ii6 I6 1 5.0
V V2 iii 1 5.0

Transitions between bass degrees#

full_grams = {
    i: S[(S != S.shift()).fillna(True)].to_list() for i, S in bd_series.items()
}
print(bd_series[1])
full_grams[1]
corpus             piece           i 
couperin_concerts  c01n01_prelude  0      0
                                   1      1
                                   2      4
                                   3      0
                                   4      1
                                   5      1
                                   6      0
                                   7      3
                                   8      4
                                   9      5
                                   10    -2
                                   11     3
                                   12     3
                                   13     1
Name: bass_note, dtype: Int64
[0, 1, 4, 0, 1, 0, 3, 4, 5, -2, 3, 1]
minor_region_selector = key_regions.localkey.str.islower()
minor_regions = key_regions[minor_region_selector].localkey.to_dict()
major_regions = key_regions[~minor_region_selector].localkey.to_dict()
full_grams_minor = [ms3.fifths2sd(full_grams[i], True) + ["∅"] for i in minor_regions]
full_grams_major = [ms3.fifths2sd(full_grams[i], False) + ["∅"] for i in major_regions]
utils.plot_transition_heatmaps(full_grams_major, full_grams_minor)
save_pdf_path = os.path.join(
    RESULTS_PATH, f"bass_degree_bigrams{utils.DEFAULT_OUTPUT_FORMAT}"
)
plt.savefig(save_pdf_path, dpi=400)
plt.show()
../_images/08095a9e3664f8a179c971d19f77de2ad254f5ec65dcc6051f7ecbd809d16e5c.png
utils.plot_transition_heatmaps(
    full_grams_major, full_grams_minor, sort_scale_degrees=True
)
save_pdf_path = os.path.join(
    RESULTS_PATH, f"bass_degree_bigrams_scale_order{utils.DEFAULT_OUTPUT_FORMAT}"
)
plt.savefig(save_pdf_path, dpi=400)
plt.show()
../_images/6da2605473e86f4b6ca137774335dfea9659ef389bd6eae1ffc31f44650aa144.png

Most frequent 2-grams#

Select number of first k transitions to display:

k = 25
Major#
utils.sorted_gram_counts(full_grams_major, 2)
N = 3077
count %
progression
(5, 1) 433 14.07
(1, 5) 242 7.86
(1, ∅) 206 6.69
(7, 1) 187 6.08
(4, 5) 159 5.17
(1, 4) 146 4.74
(5, 3) 138 4.48
(3, 4) 128 4.16
(1, 7) 126 4.09
(4, 3) 119 3.87
(3, 1) 107 3.48
(2, 1) 100 3.25
(2, 5) 97 3.15
(3, 2) 90 2.92
(4, 2) 90 2.92
(7, 5) 88 2.86
(1, 3) 88 2.86
(6, 4) 82 2.66
(1, 6) 74 2.40
(5, 6) 72 2.34
(6, 5) 71 2.31
(6, 7) 67 2.18
(3, 5) 61 1.98
(5, 4) 54 1.75
(5, 7) 52 1.69
Minor#
utils.sorted_gram_counts(full_grams_minor, 2)
N = 2994
count %
progression
(5, 1) 385 12.86
(4, 5) 249 8.32
(1, ∅) 198 6.61
(1, 5) 196 6.55
(#7, 1) 161 5.38
(1, 4) 144 4.81
(3, 2) 128 4.28
(4, 3) 126 4.21
(3, 4) 108 3.61
(6, 5) 105 3.51
(6, 4) 103 3.44
(2, 1) 101 3.37
(5, 3) 100 3.34
(5, 4) 93 3.11
(3, 1) 85 2.84
(2, 3) 84 2.81
(1, #7) 84 2.81
(4, 2) 83 2.77
(5, 6) 81 2.71
(#7, 5) 79 2.64
(2, 5) 73 2.44
(1, 6) 65 2.17
(1, 3) 61 2.04
(1, 7) 53 1.77
(1, 2) 49 1.64

Most frequent 3-grams#

Major#
utils.sorted_gram_counts(full_grams_major, 3)
N = 1483
count %
progression
(5, 1, ∅) 169 11.40
(1, 5, 1) 135 9.10
(4, 5, 1) 120 8.09
(5, 1, 5) 76 5.12
(3, 2, 1) 66 4.45
(7, 1, 5) 58 3.91
(7, 5, 1) 55 3.71
(5, 1, 7) 54 3.64
(6, 7, 1) 53 3.57
(3, 4, 5) 52 3.51
(1, 7, 1) 50 3.37
(4, 3, 2) 49 3.30
(1, 4, 5) 48 3.24
(6, 4, 5) 47 3.17
(5, 3, 1) 46 3.10
(3, 5, 1) 44 2.97
(5, 1, 4) 44 2.97
(2, 5, 1) 43 2.90
(5, 4, 3) 42 2.83
(3, 1, 4) 40 2.70
(4, 2, 5) 40 2.70
(2, 1, 5) 39 2.63
(3, 1, 5) 38 2.56
(1, 4, 2) 38 2.56
(1, 7, 6) 37 2.49
Minor#
utils.sorted_gram_counts(full_grams_minor, 3)
N = 1488
count %
progression
(5, 1, ∅) 165 11.09
(4, 5, 1) 140 9.41
(1, 5, 1) 108 7.26
(3, 2, 1) 77 5.17
(6, 4, 5) 75 5.04
(3, 4, 5) 72 4.84
(5, 4, 3) 65 4.37
(5, 1, 5) 61 4.10
(#7, 1, 5) 61 4.10
(1, 4, 5) 60 4.03
(#7, 5, 1) 59 3.97
(4, 3, 2) 58 3.90
(1, #7, 1) 54 3.63
(5, 1, 4) 46 3.09
(1, 7, 6) 43 2.89
(2, 1, 5) 39 2.62
(4, 5, 6) 37 2.49
(5, 6, 4) 36 2.42
(4, 2, 5) 35 2.35
(7, 6, 5) 34 2.28
(6, 5, 4) 34 2.28
(2, 1, #7) 33 2.22
(3, 1, 4) 32 2.15
(4, 3, 4) 32 2.15
(1, 6, 4) 32 2.15

Most frequent 4-grams#

Major#
utils.sorted_gram_counts(full_grams_major, 4)
N = 701
count %
progression
(4, 5, 1, ∅) 63 8.99
(1, 5, 1, ∅) 54 7.70
(5, 1, 5, 1) 45 6.42
(1, 4, 5, 1) 41 5.85
(6, 4, 5, 1) 39 5.56
(4, 3, 2, 1) 36 5.14
(1, 5, 1, 5) 36 5.14
(3, 4, 5, 1) 35 4.99
(7, 1, 5, 1) 28 3.99
(3, 1, 5, 1) 25 3.57
(3, 5, 1, ∅) 24 3.42
(1, 7, 6, 5) 23 3.28
(4, 5, 1, 5) 23 3.28
(3, 2, 1, 5) 22 3.14
(5, 6, 7, 1) 22 3.14
(1, 7, 1, 5) 20 2.85
(5, 1, 4, 5) 20 2.85
(5, 6, 4, 5) 19 2.71
(2, 1, 5, 1) 19 2.71
(5, 3, 1, 5) 18 2.57
(7, 1, 7, 1) 18 2.57
(7, 1, 3, 4) 18 2.57
(7, 5, 1, 7) 18 2.57
(3, 1, 4, 2) 18 2.57
(5, 3, 2, 1) 17 2.43
Minor#
utils.sorted_gram_counts(full_grams_minor, 4)
N = 744
count %
progression
(4, 5, 1, ∅) 88 11.83
(1, 5, 1, ∅) 45 6.05
(3, 4, 5, 1) 43 5.78
(1, 4, 5, 1) 43 5.78
(6, 4, 5, 1) 40 5.38
(4, 3, 2, 1) 38 5.11
(5, 4, 3, 2) 34 4.57
(5, 1, 5, 1) 33 4.44
(5, 6, 4, 5) 31 4.17
(1, 7, 6, 5) 30 4.03
(#7, 1, 5, 1) 29 3.90
(3, 2, 1, 5) 28 3.76
(3, 2, 1, #7) 25 3.36
(2, 1, 5, 1) 24 3.23
(1, #7, 1, 5) 23 3.09
(6, 5, 4, 3) 22 2.96
(5, 1, 4, 5) 22 2.96
(2, 3, 4, 5) 20 2.69
(1, 6, 4, 5) 20 2.69
(5, 1, 7, 6) 19 2.55
(2, 1, #7, 1) 19 2.55
(4, 3, 4, 5) 18 2.42
(5, 1, #7, 1) 18 2.42
(1, 5, 1, 4) 16 2.15
(5, 3, 2, 1) 16 2.15

Most frequent 5-grams#

Major#
utils.sorted_gram_counts(full_grams_major, 5)
N = 345
count %
progression
(1, 4, 5, 1, ∅) 26 7.54
(1, 5, 1, 5, 1) 24 6.96
(5, 1, 5, 1, 5) 22 6.38
(6, 4, 5, 1, ∅) 17 4.93
(2, 1, 5, 1, ∅) 17 4.93
(3, 4, 5, 1, ∅) 16 4.64
(5, 6, 4, 5, 1) 16 4.64
(5, 1, 4, 5, 1) 16 4.64
(5, 3, 1, 5, 1) 15 4.35
(7, 1, 4, 5, 1) 14 4.06
(1, 5, 6, 4, 5) 13 3.77
(7, 1, 5, 1, ∅) 12 3.48
(4, 5, 1, 5, 1) 12 3.48
(4, 3, 4, 5, 1) 11 3.19
(1, 4, 3, 2, 1) 11 3.19
(7, 5, 1, 7, 5) 11 3.19
(1, 6, 4, 5, 1) 11 3.19
(4, 3, 2, 1, 5) 11 3.19
(3, 2, 1, 5, 1) 11 3.19
(5, 4, 3, 2, 1) 10 2.90
(5, 1, 7, 5, 1) 10 2.90
(1, 7, 5, 1, 7) 10 2.90
(3, 1, 5, 1, ∅) 10 2.90
(7, 1, 7, 6, 5) 10 2.90
(1, 4, 3, 5, 1) 9 2.61
Minor#
utils.sorted_gram_counts(full_grams_minor, 5)
N = 363
count %
progression
(3, 4, 5, 1, ∅) 29 7.99
(6, 4, 5, 1, ∅) 25 6.89
(1, 4, 5, 1, ∅) 25 6.89
(3, 2, 1, 5, 1) 20 5.51
(5, 4, 3, 2, 1) 19 5.23
(5, 6, 4, 5, 1) 18 4.96
(5, 1, 4, 5, 1) 18 4.96
(4, 3, 4, 5, 1) 15 4.13
(3, 2, 1, #7, 1) 15 4.13
(4, 3, 2, 1, 5) 14 3.86
(4, 3, 2, 1, #7) 13 3.58
(6, 5, 4, 3, 2) 13 3.58
(1, 5, 1, #7, 1) 12 3.31
(5, 1, 5, 1, ∅) 12 3.31
(5, 1, #7, 1, 5) 11 3.03
(#7, 1, 5, 1, ∅) 11 3.03
(2, 1, 5, 1, ∅) 11 3.03
(5, 1, 7, 6, 5) 11 3.03
(3, 1, 4, 2, 5) 11 3.03
(4, 5, 6, 4, 5) 10 2.75
(#7, 5, 1, 4, 5) 10 2.75
(2, 3, 4, 5, 1) 10 2.75
(#7, 1, 3, 4, 5) 10 2.75
(1, 3, 4, 5, 1) 10 2.75
(1, 4, 3, 2, 1) 10 2.75